package logic;

import java.util.ArrayList;
import weka.classifiers.Classifier;
import weka.core.Capabilities;
import weka.core.Capabilities.Capability;
import weka.core.Instance;
import weka.core.Instances;

public abstract class MushroomClassifier extends Classifier {
    public Instances dataSet;
    
    public String algo;
    
    public MushroomClassifier() {
        
    }
    
    public static String getUsedAttributes(Instances data) {
        StringBuilder sb = new StringBuilder();
        
        for (int i = 0; i < MushroomInfo.NUM_ATTRIBUTES; i++) {
            String atrName = MushroomInfo.ATTRIBUTE_NAMES[i];
            for (int j = 0; j < data.numAttributes(); j++) {
                if (atrName.equals(data.attribute(j).name())) {
                    sb.append("1");
                    break;
                }
            }
            if (sb.length() < i+1) {
                sb.append("0");
            }
        }
        
        return sb.toString();
    }
    
    public Instances[] percentageSplit(Instances insts, int percent) {
        Instances[] result = new Instances[2];
        for (int i = 0; i < result.length; i++) {
            result[i] = new Instances(insts);
        }
        
        int numInst = insts.numInstances();
        int numTraining = (numInst*percent)/100;
        int numTest = numInst - numTraining;
        ArrayList<Integer> toTraining = new ArrayList<Integer>();
        
        for (int i = 0; i < numTraining; i++) {
            int rand = (int)(Math.random()*numInst);
            while (toTraining.contains(rand)) {
                if (rand == (numInst - 1))
                    rand = 0;
                else
                    rand++;
            }
            insertSequential(toTraining, rand);
        }
        
        for (int i = numInst - 1; i >= 0; i--) {
            if (toTraining.contains(i))
                result[1].delete(i);
            else
                result[0].delete(i);
        }
        
        return result;
    }
    
    public void insertSequential(ArrayList<Integer> ints, int val) {
        for (int i = 0; i < ints.size(); i++) {
            if (val < ints.get(i)) {
                ints.add(i, val);
                return;
            }
        }
        ints.add(val);
    }
    
    @Override
    public Capabilities getCapabilities() {
        Capabilities result = new Capabilities(this);
        
        result.enable(Capability.NOMINAL_ATTRIBUTES);
        result.enable(Capability.MISSING_VALUES);
        
        result.enable(Capability.NOMINAL_CLASS);
        
        return result;
    }
    
    @Override
    public double classifyInstance(Instance i) {
        return dataSet.classAttribute().indexOfValue(classify(i)+"");
    }
    
    public double accuracy(Instances insts) {
        int count = 0;
        int numInst = insts.numInstances();
        
        for (int i = 0; i < numInst; i++) {
            Instance in = insts.instance(i);
            try {
                if (classifyInstance(in) == in.classValue()) {
                    count++;
                }
            } catch (Exception e) {
                System.err.println("Exception in MushroomClassifier.accuracy()");
            }
        }
        
        return ((double)count)/insts.numInstances();
    }
    
    public static double accuracy(Classifier cls, Instances insts) {
        int count = 0;
        int numInst = insts.numInstances();
        
        for (int i = 0; i < numInst; i++) {
            Instance in = insts.instance(i);
            try {
                if (cls.classifyInstance(in) == in.classValue()) {
                    count++;
                }
            } catch (Exception e) {
                System.err.println("Exception in MushroomClassifier.accuracy()");
            }
        }
        
        return ((double)count)/insts.numInstances();
    }
    
    public static Instance createInstance(char[] str, Instances dataset) {
        StringBuilder s = new StringBuilder();
        for (char c : str) {
            s.append(c);
        }
        
        String st = s.toString();
        return createInstance(st, dataset);
    }
    public static Instance createInstance(String str, Instances dataset) {
        Instance in = new Instance(dataset.firstInstance());
        in.setDataset(dataset);
        
        for (int i = 0; i < in.numAttributes() - 1; i++) {
            in.setValue(i, str.charAt(i)+"");
        }
        in.setClassMissing();
        
        return in;
    }
    
    public abstract char classify(Instance in);
    
    public abstract void learn(Instance i);
    
    public Instance initTestInstance(Instance in) {
        Instance i = new Instance(in);
        i.setDataset(dataSet);
        i.setClassMissing();
        return i;
    }
}
